// RFC7798 RTP Payload Format for High Efficiency Video Coding (HEVC)

#include "rtp-packet.h"
#include "rtp-payload-internal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>

/*
0               1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|    Type   |  LayerId  | TID |
+-------------+-----------------+

Forbidden zero(F) : 1 bit
NAL unit type(Type) : 6 bits
NUH layer ID(LayerId) : 6 bits
NUH temporal ID plus 1 (TID) : 3 bits
*/

#define H265_TYPE(v) (((v) >> 1) & 0x3f)

#define FU_START(v) ((v)&0x80)
#define FU_END(v)   ((v)&0x40)
#define FU_NAL(v)   ((v)&0x3F)

struct rtp_decode_h265_t {
    struct rtp_payload_t handler;
    void *cbparam;

    uint16_t seq; // rtp seq
    uint32_t timestamp;

    uint8_t *ptr;
    int size, capacity;

    int flags;
    int using_donl_field;
};

static void *rtp_h265_unpack_create(struct rtp_payload_t *handler, void *param)
{
    struct rtp_decode_h265_t *unpacker;
    unpacker = (struct rtp_decode_h265_t *)calloc(1, sizeof(*unpacker));
    if (!unpacker)
        return NULL;

    memcpy(&unpacker->handler, handler, sizeof(unpacker->handler));
    unpacker->cbparam = param;
    unpacker->flags = -1;
    return unpacker;
}

static void rtp_h265_unpack_destroy(void *p)
{
    struct rtp_decode_h265_t *unpacker;
    unpacker = (struct rtp_decode_h265_t *)p;

    if (unpacker->ptr)
        free(unpacker->ptr);
#if defined(_DEBUG) || defined(DEBUG)
    memset(unpacker, 0xCC, sizeof(*unpacker));
#endif
    free(unpacker);
}

// 4.4.2. Aggregation Packets (APs) (p25)
/*
 0               1               2               3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          RTP Header                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      PayloadHdr (Type=48)     |           NALU 1 DONL         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           NALU 1 Size         |            NALU 1 HDR         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                         NALU 1 Data . . .                     |
|                                                               |
+     . . .     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |  NALU 2 DOND  |            NALU 2 Size        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          NALU 2 HDR           |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+            NALU 2 Data        |
|                                                               |
|         . . .                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :    ...OPTIONAL RTP padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
static int rtp_h265_unpack_ap(struct rtp_decode_h265_t *unpacker, const uint8_t *ptr, int bytes, uint32_t timestamp)
{
    int r;
    int n;
    int len;
    // uint16_t donl;
    // uint16_t dond;

    // donl = unpacker->using_donl_field ? nbo_r16(ptr + 2) : 0;
    ptr += 2; // PayloadHdr
    n = 2 /*LEN*/ + (unpacker->using_donl_field ? 2 : 0);
    r = 0;

    for (bytes -= 2 /*PayloadHdr*/; 0 == r && bytes > n; bytes -= len + 2) {
        bytes -= n - 2; // skip DON
        ptr += n - 2;   // skip DON
        len = nbo_r16(ptr);
        if (len + 2 > bytes || len < 3) {
            assert(0);
            unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
            unpacker->size = 0;
            return -EINVAL; // error
        }

        assert(H265_TYPE(ptr[2]) >= 0 && H265_TYPE(ptr[2]) < 48);
        r = unpacker->handler.packet(unpacker->cbparam, ptr + 2, len, timestamp, unpacker->flags);
        unpacker->flags = 0;
        unpacker->size = 0;

        ptr += len + 2; // next NALU
        n = 2 /*LEN*/ + (unpacker->using_donl_field ? 1 : 0);
    }

    return 0 == r ? 1 : r; // packet handled
}

// 4.4.3. Fragmentation Units (p29)
/*
 0               1               2               3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     PayloadHdr (Type=49)      |    FU header  |  DONL (cond)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|  DONL (cond)  |                                               |
|-+-+-+-+-+-+-+-+                                               |
|                           FU payload                          |
|                                                               |
|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :    ...OPTIONAL RTP padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|   FuType  |
+---------------+
*/
static int rtp_h265_unpack_fu(struct rtp_decode_h265_t *unpacker, const uint8_t *ptr, int bytes, uint32_t timestamp)
{
    int r, n;
    uint8_t fuheader;

    r = 0;
    n = 1 /*FU header*/ + (unpacker->using_donl_field ? 4 : 2);
    if (bytes < n || unpacker->size + bytes - n > RTP_PAYLOAD_MAX_SIZE) {
        assert(0);
        return -EINVAL;
    }

    if (unpacker->size + bytes - n + 2 /*NALU*/ > unpacker->capacity) {
        void *p = NULL;
        int size = unpacker->size + bytes + 2;
        size += size / 4 > 128000 ? size / 4 : 128000;
        p = realloc(unpacker->ptr, size);
        if (!p) {
            // set packet lost flag
            unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
            unpacker->size = 0;
            return -ENOMEM;
        }
        unpacker->ptr = (uint8_t *)p;
        unpacker->capacity = size;
    }

    fuheader = ptr[2];
    if (FU_START(fuheader)) {
#if 0
		if (unpacker->size > 0)
		{
			unpacker->flags |= RTP_PAYLOAD_FLAG_PACKET_CORRUPT;
			unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, unpacker->timestamp, unpacker->flags);
			unpacker->flags = 0;
			unpacker->size = 0; // reset
		}
#endif

        assert(unpacker->capacity > 2);
        unpacker->size = 2;                                           // NAL unit type byte
        unpacker->ptr[0] = (FU_NAL(fuheader) << 1) | (ptr[0] & 0x81); // replace NAL Unit Type Bits
        unpacker->ptr[1] = ptr[1];
        assert(H265_TYPE(unpacker->ptr[0]) >= 0 && H265_TYPE(unpacker->ptr[0]) <= 63);
    } else {
        if (0 == unpacker->size) {
            unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
            return 0; // packet discard
        }
        assert(unpacker->size > 0);
    }

    unpacker->timestamp = timestamp;
    if (bytes > n) {
        assert(unpacker->capacity >= unpacker->size + bytes - n);
        memmove(unpacker->ptr + unpacker->size, ptr + n, bytes - n);
        unpacker->size += bytes - n;
    }

    if (FU_END(fuheader)) {
        r = unpacker->handler.packet(unpacker->cbparam, unpacker->ptr, unpacker->size, timestamp, unpacker->flags);
        unpacker->flags = 0;
        unpacker->size = 0;
    }

    return 0 == r ? 1 : r; // packet handled
}

static int rtp_h265_unpack_input(void *p, const void *packet, int bytes)
{
    int r, nal;
    const uint8_t *ptr;
    struct rtp_packet_t pkt;
    struct rtp_decode_h265_t *unpacker;

    unpacker = (struct rtp_decode_h265_t *)p;
    if (!unpacker || 0 != rtp_packet_deserialize(&pkt, packet, bytes) ||
        pkt.payloadlen < (unpacker->using_donl_field ? 5 : 3))
        return -EINVAL;

    if (-1 == unpacker->flags) {
        unpacker->flags = 0;
        unpacker->seq = (uint16_t)(pkt.rtp.seq - 1); // disable packet lost
    }

    if ((uint16_t)pkt.rtp.seq != (uint16_t)(unpacker->seq + 1)) {
        unpacker->flags = RTP_PAYLOAD_FLAG_PACKET_LOST;
        unpacker->size = 0; // discard previous packets
    }
    unpacker->seq = (uint16_t)pkt.rtp.seq;

    assert(pkt.payloadlen > 2);
    ptr = (const uint8_t *)pkt.payload;
    nal = H265_TYPE(ptr[0]);

    if (nal > 50)
        return 0; // packet discard, Unsupported (HEVC) NAL type

    switch (nal) {
    case 48: // aggregated packet (AP) - with two or more NAL units
        return rtp_h265_unpack_ap(unpacker, ptr, pkt.payloadlen, pkt.rtp.timestamp);

    case 49: // fragmentation unit (FU)
        return rtp_h265_unpack_fu(unpacker, ptr, pkt.payloadlen, pkt.rtp.timestamp);

    case 50: // TODO: 4.4.4. PACI Packets (p32)
        assert(0);
        return 0; // packet discard

    case 32: // video parameter set (VPS)
    case 33: // sequence parameter set (SPS)
    case 34: // picture parameter set (PPS)
    case 39: // supplemental enhancement information (SEI)
    default: // 4.4.1. Single NAL Unit Packets (p24)
        r = unpacker->handler.packet(unpacker->cbparam, ptr, pkt.payloadlen, pkt.rtp.timestamp, unpacker->flags);
        unpacker->flags = 0;
        unpacker->size = 0;
        return 0 == r ? 1 : r; // packet handled
    }
}

struct rtp_payload_decode_t *rtp_h265_decode()
{
    static struct rtp_payload_decode_t unpacker = {
        rtp_h265_unpack_create,
        rtp_h265_unpack_destroy,
        rtp_h265_unpack_input,
    };

    return &unpacker;
}
